/* Monte_Carlo.h
 *
 *	Copyright (C) 2008 CRIMERE
 *	Copyright (C) 2008 Jean-Marc Mercier
 *	
 *	This file is part of OTS (optimally transported schemes), an open-source library
 *	dedicated to scientific computing. http://code.google.com/p/optimally-transported-schemes/
 *
 *	CRIMERE makes no representations about the suitability of this
 *	software for any purpose. It is provided "as is" without express or
 *	implied warranty.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *
 */
#ifndef _OS_Monte_Carlo_
#define _OS_Monte_Carlo_

#include <src/miscellaneous/OS_basic.h>
#include <src/miscellaneous/Function_Utilities.h>

/*! Monte Carlo method prototype
	
	Base class for Monte Carlo method, that is
	\f[ \sum_{i=1,..,N} \mu_i f(x_i) \f]
	where \f$ f(x) \f$ is any function, and \f$ \{ \mu_i\}_{i} \f$ are the weights.
*/
template<class function_type, class generator_type>
class OS_Monte_Carlo_Base
{
public :
	typedef function_type function_type;
	typedef generator_type generator_type;
	OS_Monte_Carlo_Base(){};
	typedef typename function_type::argument_type	argument_type;
	typedef typename function_type::result_type		result_type;
	virtual result_type operator()(OS_size nb_samples)  = 0; // const cannot be const due to the generator
	generator_type	generator_;
	function_type	function_;
};
/*! Monte Carlo method prototype for the equiweighted case
	
	Base class for equiweighted Monte Carlo method, that is
	\f[ \frac{1}{N} \sum_{i=1,..,N} f(x_i) \f]
	where \f$ f(x) \f$ is any function.
*/
template<class function_type, class generator_type>
class OS_Monte_Carlo : public OS_Monte_Carlo_Base<function_type,generator_type>
{
public :
	typedef OS_Monte_Carlo_Base Base;
	OS_Monte_Carlo() : Base(){};
	virtual result_type operator()(OS_size nb_samples) // const cannot be const due to the generator
	{
		argument_type sample(generator_());
		result_type value(function_(sample));
		result_type result(value);
//			std::cout << std::endl << "---------Starting MonteCarlo sampling------------- " << std::endl;
//			std::cout << " sample " << sample << " evaluation "<< value << "    ";
		for (OS_size i=1;i<nb_samples;++i) {
			sample = generator_();
			value = function_(sample);
//			std::cout << " sample " << sample << " evaluation "<< value << "    ";
			result += value;
		};
//		std::cout << std::endl << "----------End of Monte Carlo. evaluation: " << (result / nb_samples) << "--------------"<< std::endl;
		return (result / nb_samples);
	};
};

/* Helper to build Monte Carlo Pricer for filtered processes
*/
template<class payoff, class process, class generator >
class European_Pricer : public OS_Monte_Carlo<smart_ptr_function<payoff>,generator >
{
public:
	typedef OS_Monte_Carlo													Base;
	typedef typename Base::generator_type									generator_type;
	typedef payoff															payoff_type;
	typedef process															stochastic_process_type;

	European_Pricer(smart_ptr<stochastic_process_type> & process, smart_ptr<payoff> & payoff) : 
		process_(process), payoff_(payoff) 
	{
		function_.set_function(payoff);
		generator_ = generator_type(process,payoff);
	};
	virtual double operator()(unsigned long Samples_nb) {
		generator_.set_steps(Samples_nb);
		double maturity = payoff_->get_maturity();
		OS_double compound = process_->compound(maturity);
		OS_double expectation = Base::operator ()(generator_.get_steps());
		return expectation * compound;
	};
	smart_ptr<payoff_type>							payoff_;
	smart_ptr<stochastic_process_type>				process_;
};

#endif
